package app

import akka.actor.typed.{ActorRef, ActorSystem, Behavior, Scheduler}
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Route
import akka.util.Timeout
import actors.Messages._
import actors.Registry._
import akka.actor.typed.scaladsl.AskPattern.Askable
import http.RegistryRoutes

import java.net.InetSocketAddress
import scala.concurrent.duration.DurationInt
import scala.concurrent.{ExecutionContext, Future}
import scala.language.postfixOps
import scala.util.{Failure, Success}

object RaidenApp {

  def startHttpServer(master: ActorRef[MasterProtocol])(implicit system: ActorSystem[_]): Unit = {
    implicit val ec: ExecutionContext = system.executionContext
    val router = new RegistryRoutes(master)
    val routes: Route = router.routes

    val httpBindingFuture: Future[Http.ServerBinding] = Http().newServerAt("localhost", 8080).bind(routes)
    httpBindingFuture.onComplete {
      case Success(binding) =>
        val address: InetSocketAddress = binding.localAddress
        system.log.info(s"Server online at https://${address.getHostString}:${address.getPort}")
      case Failure(ex) =>
        system.log.error(s"Failed to bind HTTP server, because $ex")
        system.terminate()
    }
  }

  def main(args: Array[String]): Unit = {
    trait RootCommand
    case class RetrieveMasterActor(replyTo: ActorRef[ActorRef[MasterProtocol]]) extends RootCommand

    val rootBehavior: Behavior[RootCommand] = Behaviors.setup { context: ActorContext[RootCommand] =>
      val masterActor: ActorRef[MasterProtocol] = context.spawn(RegistryMaster(), "master")

      Behaviors.receiveMessage {
        case RetrieveMasterActor(replyTo) =>
          replyTo ! masterActor
          Behaviors.same
      }
    }

    implicit val system: ActorSystem[RootCommand] = ActorSystem(rootBehavior, "DeviceSystem")
    implicit val timeout: Timeout = Timeout(10 seconds)
    implicit val scheduler: Scheduler = system.scheduler
    implicit val ec: ExecutionContext = system.executionContext

    val masterActorFuture: Future[ActorRef[MasterProtocol]] = system.ask(
      (replyTo: ActorRef[ActorRef[MasterProtocol]]) => RetrieveMasterActor(replyTo)
    )
    masterActorFuture.foreach(startHttpServer)
  }
}
